import * as React from 'react';
import {
  Routes,
  Route,
  Link,
  useNavigate,
  useLocation,
  Navigate,
  Outlet,
} from 'react-router-dom';

import { fakeAuthProvider } from './auth';


/* 根组件 */
export default function App() {
  return (
    // 全局透传AuthContext
    <AuthProvider>

      {/* App给AuthProvider传入的props.children[0] */}
      <h1>鉴权样例</h1>

      {/* App给AuthProvider传入的props.children[1] */}
      <Routes>
        {/* Layout是App的主视图 */}
        <Route element={<Layout />}>

          {/* 无守卫的公共组件 */}
          <Route path="/" element={<PublicPage />} />

          {/* 无守卫的登录组件 */}
          <Route path="/login" element={<LoginPage />} />

          {/* 受保护路由【守卫器组件】 */}
          <Route
            path="/protected"
            element={
              <RequireAuth>
                {/* 被守卫的组件是【守卫器组件】的children */}
                <ProtectedPage />
              </RequireAuth>
            }
          />

        </Route>
      </Routes>

    </AuthProvider>
  );
}

/* App的主体组件 */
function Layout() {
  return (
    <div>
      <AuthStatus />

      <ul>
        <li>
          <Link to="/">公共页面</Link>
        </li>
        <li>
          <Link to="/protected">受保护页面e</Link>
        </li>
      </ul>

      <Outlet />
    </div>
  );
}

// 要全局透传的Context
let AuthContext = React.createContext(null);

/* AuthContext.Provider */
// 接收由父组件注入的props 只解构出childre（h3+routes[layout[公共页+登录页+守卫器[被守卫页]]]）
function AuthProvider({ children }) {

  // 相当于类组件中：this.state={user:null}
  // 而且state有响应式
  let [user, setUser] = React.useState(null);

  // 登录方法：newUser为用户输入，callback未知
  let signin = (newUser, callback) => {
    // 实际调用auth.js中的登录方法
    return fakeAuthProvider.signin(() => {
      // 登录成功后setState({user:newUser})
      // 全局依赖user数据的组件都会更新
      setUser(newUser);

      // 事实上
      callback();
    });
  };

  // 登录方法
  let signout = (callback) => {
    return fakeAuthProvider.signout(() => {
      // 设置当前用户为null
      setUser(null);
      // 执行回调（事实上返回首页）
      callback();
    });
  };

  // 将【响应式的用户数据 + 登录方法 + 登出方法】透传全局
  // => 所有子组件都能动态渲染用户数据 + 执行登录 + 执行登出
  let value = { user, signin, signout };

  return <AuthContext.Provider value={value}>
    {children}
  </AuthContext.Provider>;
}

/* 拿到全局透传的AuthContext的value */
function useAuth() {
  return React.useContext(AuthContext);
}

/* 显示用户的登录状态 */
// AuthStatus.contextType = AuthContext
function AuthStatus() {

  // 拿到全局透传下来的【当前登录用户】+登入/登出API
  // let auth = useAuth();
  let authContextValue = React.useContext(AuthContext);

  // 登出后跳转首页
  let navigate = useNavigate();

  /* 显示未登录 */
  if (!authContextValue.user) {
    return <p>还未登录！</p>;
  }

  return (
    <p>
      欢迎 {authContextValue.user}!{' '}

      {/* 点击登出 执行登出 然后返回首页 */}
      <button
        onClick={() => {
          authContextValue.signout(() => navigate('/'));
        }}
      >
        登出
      </button>
    </p>
  );
}

// 我爹给我传入的children就是【受保护的页面】
function RequireAuth({ children }) {
  // let auth = useAuth();
  let authContextValue = React.useContext(AuthContext);
  let location = useLocation();

  /* 无登录用户信息 显示登录组件 */
  if (!authContextValue.user) {
    // 重定向至/login，携带一个from状态（以便登录成功后跳转过去），replace=login页面原地替换哥自己
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  // 直接渲染【受保护的页面】ProtectedPage
  return children;
}

/*  */
function LoginPage() {

  // 登录成功后要跳转来源页
  let navigate = useNavigate();

  // 作为重定向的目标页 准备获取Navigate过来时的state
  let location = useLocation();

  // 拿到全局透传的【用户大礼包】 稍后调用signin方法 登录成功后跳转来源页
  let authContextValue = useAuth();

  // 拿到RequireAuth传递的from数据 失败则将首页作为来源页
  // <Navigate to="/login" state={{from:location}} />
  let from = location.state?.from?.pathname || '/';

  /* 处理登录表单提交事件 */
  function handleSubmit(event) {
    // 阻止默认行为
    event.preventDefault();

    // {username：xxx}
    let formData = new FormData(event.currentTarget);
    let username = formData.get('username');

    // 调用AuthContext大礼包中的登录方法
    authContextValue.signin(
      username,
      () => {
        // 送用户回去他们试图访问的页面
        // 使用 { replace: true } 保证我们不会把login放入history栈
        // 意味着当用户点击回退，他不会重新回退到login页面
        navigate(from, { replace: true });
      }
    );
  }

  return (
    <div>
      {/* 你必须登录去查看/protected页面 */}
      <p>你必须登录去查看{from}页面</p>

      <form onSubmit={handleSubmit}>
        <label>
          Username: <input name="username" type="text" />
        </label>{' '}
        <button type="submit">Login</button>
      </form>

    </div>
  );
}

/* 无守护公共页面 */
function PublicPage() {
  return <h3>这是公共页面</h3>;
}

/* 受保护的页面 */
function ProtectedPage() {
  return <h3>这是受保护页面</h3>;
}
